package cn.jilian.entity;

import java.sql.Types;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.PostLoad;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Sets;

import cn.jilian.common.config.SpringBeans;
import cn.jilian.common.util.MapUtil;
import cn.jilian.meta.service.MetaService;

@Entity
@Table(name="bt_meta",uniqueConstraints=@UniqueConstraint(name="metaByMidAndCodeAndRankss",columnNames = {"mid","rank", "code" }))
public class Meta{
	
	public static final int FIELD_MODEL_RANK=210309;
	
	public static final int ATTR_MODEL_RANK=210401;
	
	public static final Long ENUM_META=539103L;
	
	public static final Long GROUP_META=539102L;
	
	public static final Long ENTITY_MODEL_META=539107L;
	
	public static final Long TRAIT_MODEL_META=539108L;
	
	public static final Long MODULE_META=539106L;
	
	public static final Long MODULE_ROW_META=539122L;
	
	public static final Long MODULE_COL_META=539121L;
	
	public static final Long TABLE_META=539101L;
	
	public static final Long VIEW_META=539105L;
	
	public static final Long QUERY_META=539104L;
	
	public static final Long UNION_META=539109L;
	
	public static final String REFMODELID="refModelId";
	
	public static final String BIND_CLASS="bindClass";
	
	public static final String SEARCH_FIELDS="searchFields";
	
	public static final String ACTION_URL="action.url";
	
	public static final String ACTION_TYPE="action.type";
	
	public static final String EXCLUDED_FIELDS="excludedFields";
	
	public static final String KEY_COLUMN="keyColumn";
	
	public static final String VALUE_COLUMN="valueColumn";
	
	public static final String PROXY_TYPE="proxy.type";
	
	public static final String ADDRECORDURL="create.url";
	
	public static final String CREATE_METHOD="create.method";
	
	public static final String UPDTAERECORDURL="update.url";
	
	public static final String UPDATE_METHOD="update.method";
	
	public static final String DELETBYIDURL="destroy.url";
	
	public static final String DELETEBYID_METHOD="destroy.method";
	
	public static final String PAGEQUERYURL="query.url";
	
	public static final String QUERY_METHOD="query.method";
	
	public static final String LOADALLDATAURL="dataUrl";
	
	public static final String TYPEPROPERTY="typeProperty";
	
	public static final String IDPROPERTY="idProperty";
	
	public static  final String PARENTIDPROPERTY="parentIdProperty";
	
	public static final String ORDERSEQPRPERTY="seqProperty";
	
	public static final String MAIN_MODEL_ID="mainModelId";
	
	public static final String MODULE_PROPERTY="moduleProperty";
	
	public static final String IDSEQGENERATOR="idSeqGenerator";
	
	/**
	 * 子类模型
	 * */
	public static final String CHILDTYPE="meta.childType";
	
	/**
	 * 属性配置
	 * */
	public static final String ATTR_RANK="rank.attr";
	
	/**
	 * 权限配置
	 * */
	public static final String MACE_RANK="rank.mace";
	
	/**
	 * 数据存储
	 * */
	public static final String STORE_RANK="storeModuleId";
	
	
	/**
	 * 模式约束
	 * */
	public static final String CONSTRAINS_RANK="rank.constrains";
	
	/**
	 * 命名查询
	 * */
	public static final String NAME_QUERYS_STORE_RANK="rank.queries";
	
	public static final String PARENTIDPROPERTY_DEFAULT="mid";
	
	/**
	 * ID
	 * */ 
	@Id
	@GeneratedValue(generator="seq_sid")
	@Column(name="sid")
	private Long id;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}
	
	@Column(name="mid")
	private Long fid;
	
	private Integer seq;

	private String name;
	
	private String code;
	
	
	private Integer rank;
	
	@Column(name="meta")
	private Long metaId;
	
	
	private String text;
	
	private String mark;
	
	private Integer flag=1;
	
	@Transient
	private List<MField> fields;
	
	@JsonIgnore
	@Transient
	private Map<String,MField> nameFieldMap;
	
	
	
	@Transient
	private Map<String,Object> attrs;
	
	@Transient
	private Collection<Long> children;
	
	@PostLoad
	public void postLoad() {
		MetaService metaService=SpringBeans.getBean(MetaService.class);
		this.fields=metaService.getFields(id, Meta.FIELD_MODEL_RANK);
		this.attrs=metaService.getAttrs(id, Meta.ATTR_MODEL_RANK);
		if(getMetaId().equals(Meta.VIEW_META)) {
			this.fields=new LinkedList<>();
			Long refMetaId=getAttr(Meta.REFMODELID,getFid());
			Meta refModel=metaService.getMetaById(refMetaId);
			List<MField> fs=refModel.getFields();
			if(CollectionUtils.isEmpty(this.fields)) {
				Object excludedFields=getAttr(Meta.EXCLUDED_FIELDS);
				if(excludedFields!=null) {
					String[] exfields=(String[])excludedFields;
					Set<String> exfs=Sets.newHashSet(exfields);
					for(MField f:fs) {
						if(!exfs.contains(f.getName())) {
							fields.add(f);
						}
					}
				}
			}else {
				this.fields.addAll(fs);
			}
			MapUtil.applyIfDeep(this.attrs, refModel.getAttrs());
		}else if(getMetaId().equals(Meta.MODULE_COL_META)||getMetaId().equals(Meta.MODULE_ROW_META)) {
			if(this.fields==null) {
				this.fields=new LinkedList<>();
			}
			if(this.fields.size()==0) {
				List<Meta> cs=metaService.querySubMetas(id);
				for(Meta m:cs) {
					List<MField> fs=m.getFields();
					fields.addAll(fs);
				}
			}
			
		}
		if(CollectionUtils.isNotEmpty(this.fields)) {
			this.nameFieldMap=new HashMap<>();
			for(MField f:this.fields) {
				if(f.getType().equals(Types.JAVA_OBJECT)&&f.containsAttr(MField.REFMODELID)) {
					Long refModelId=((Number)f.getAttr(MField.REFMODELID)).longValue();
					Meta refModel=metaService.getMetaById(refModelId);
					List<MField> fs=refModel.getFields();
					for(MField x:fs) {
						String k=new StringBuilder(f.getName())
								.append(".")
								.append(x.getName()).toString();
						this.nameFieldMap.put(k, f);
						continue;
					}
				}
				
				this.nameFieldMap.put(f.getName(), f);
			}
		}
		
		
	}
	
	
	
	
	
	public Boolean containsKey(String key) {
		if(attrs!=null) {
			return attrs.containsKey(key);
		}
		return Boolean.FALSE;
	}
	
	public Object getAttr(String key) {
		if(attrs==null)return null;
		return attrs.get(key);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getAttr(String key,T defaultValue) {
		Object v=getAttr(key);
		if(v==null) {
			v=defaultValue;
		}
		return (T) v;
	}
	
	public Object get(String key) {
		if(attrs!=null) {
			return attrs.get(key);
		}
		return null;
	}
	
	public MField getField(String key) {
		if(this.nameFieldMap==null)return null;
		return this.nameFieldMap.get(key);
	}
	
	@JsonIgnore
	public MField getPKField() {
		if(CollectionUtils.isEmpty(this.fields))return null;
		String idProperty=(String) this.get(IDPROPERTY);
		if(StringUtils.isBlank(idProperty)&&this.nameFieldMap!=null) {
			MField f= this.nameFieldMap.get("SID");
			if(f==null) {
				f= this.nameFieldMap.get("sid");
				if(f==null) {
					f=this.nameFieldMap.get("id");
				}
			}
			if(f!=null) {
				this.attrs.put(IDPROPERTY, f.getName());
			}
			return f;
		}
		return this.getNameFieldMap().get(idProperty);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getText() {
		return text;
	}

	public void setText(String text) {
		this.text = text;
	}


	public Long getFid() {
		return fid;
	}

	public void setFid(Long fid) {
		this.fid = fid;
	}

	public Integer getSeq() {
		return seq;
	}

	public void setSeq(Integer seq) {
		this.seq = seq;
	}

	public Integer getRank() {
		return rank;
	}

	public void setRank(Integer rank) {
		this.rank = rank;
	}

	public Long getMetaId() {
		return metaId;
	}

	public void setMetaId(Long metaId) {
		this.metaId = metaId;
	}

	public Integer getFlag() {
		return flag;
	}

	public void setFlag(Integer flag) {
		this.flag = flag;
	}


	@JsonIgnore
	public Map<String, MField> getNameFieldMap() {
		return nameFieldMap;
	}

	public void setNameFieldMap(Map<String, MField> nameFieldMap) {
		this.nameFieldMap = nameFieldMap;
	}

	public List<MField> getFields() {
		return fields;
	}

	public void setFields(List<MField> fields) {
		this.fields = fields;
	}

	
	public Map<String, Object> getAttrs() {
		return attrs;
	}

	public void setAttrs(Map<String, Object> attrs) {
		this.attrs = attrs;
	}

	public String getMark() {
		return mark;
	}

	public void setMark(String mark) {
		this.mark = mark;
	}

	public Collection<Long> getChildren() {
		return children;
	}

	public void setChildren(Collection<Long> children) {
		this.children = children;
	}

	
	
}
